﻿/**
 * \file Carte.cs
 * \brief Classe Carte
 * \author ARTUR Vincent
 * \author LAPERDRIX Pierre
 * \version 1.0
 */
namespace Implem
{
	using Interfaces;
	using System;
	using System.Collections;
    using System.Collections.Generic;
	using System.Linq;
	using System.Text;
    using System.IO;
    using Wrapper;

    /**
     * \class Carte
     * \brief Objet representant la carte du jeu
     */
	public class Carte : ICarte
	{
     
        /**!< Constantes pour la sauvegarde */
        private const char PLAINE = 'p';
        private const char MONTAGNE = 'm';
        private const char DESERT = 'd';
        private const char PLAINEFER = 'a';
        private const char MONTAGNEFER = 'z';
        private const char DESERTFER = 'e';
        private const char PLAINEFRUIT = 'r';
        private const char MONTAGNEFRUIT = 't';
        private const char DESERTFRUIT = 'y';

        /*!< Matrice statique de cases de la carte */
        private static Case[][] _cases;

        /*!< Propriete accedant a la matrice _cases */
        public static Case[][] Cases
        {
			get{return _cases;}
			set{_cases=value;}
		}


        /*!< Hachage statique contenant les unites presentes sur la carte (cle = position, valeur = unite) */
		private static Dictionary<Position,List<Unite>> _unites = new Dictionary<Position,List<Unite>>();

        /*!< Propriete accedant au hachage _unites */      
        public static Dictionary<Position,List<Unite>> Unites
		{
            get { return _unites; }
            set { _unites = value; }
		}


        /*!< Hachage statique contenant les villes presentes sur la carte (cle = position, valeur = ville) */
		private static Dictionary<Position,Ville> _villes = new Dictionary<Position,Ville>();

        /*!< Propriete accedant au hachage _villes */
        public static Dictionary<Position,Ville> Villes
		{
            get { return _villes; }
            set { _villes = value; }
		}


        /*!< Strategie statique souhaitee pour la generation de la carte (petite, moyenne ou grande) */
		private static StrategieCarte _strat;

        /*!< Propriete accedant a l'attribut _strat */
        public static StrategieCarte Strat
		{
            get { return _strat; }
            set { _strat = value; }
		}


        
        /**
         * \fn public Carte(StrategieCarte strategie)
         * \brief Constructeur de Carte
         * \param strategie la strategie de generation de carte
         */
        public Carte(StrategieCarte strategie)
        {
            Strat = strategie;
            _cases = Strat.genererCarte();
        }


        /**
         * \fn public static Case getCase(Position p)
         * \brief Retourne la case situee a la position p
         * \param p la position
         * \return la case
         */
        public static Case getCase(Position p)
		{
            return _cases[p.X][p.Y];
		}

        public unsafe static List<Position> suggestion()
        {
            WSugg ws = new WSugg();
            List<Position> lres = new List<Position>();

            // Matrice de cases
            int** cases = stackalloc int*[Cases.Length];
            for (int i = 0; i < Cases.Length; i++)
            {
                int* lol = stackalloc int[Cases.Length];
                cases[i] = lol;
                for (int j = 0; j < Cases.Length; j++)
                {
                    cases[i][j] = Cases[i][j].Minerai + Cases[i][j].Nourriture;
                }
            }

            // Matrice d'ennemis
            int** ennemis = stackalloc int*[Cases.Length];
            for (int i = 0; i < Cases.Length; i++)
            {
                int* lol = stackalloc int[Cases.Length];
                ennemis[i] = lol;
                for (int j = 0; j < Cases.Length; j++)
                {
                    Position tmp = new Position(i, j);
                    ennemis[i][j] = 0;
                    if(Unites.ContainsKey(tmp))
                    {
                        foreach (Unite u in Unites[tmp])
                        {
                            if (!(u.JoueurProprietaire.Equals(Partie.joueurCourant))) ennemis[i][j] += u.Attaque;
                            
                        }
                    }
                }
            }

            // Matrice de villes
            int** villes = stackalloc int*[Cases.Length];
            for (int i = 0; i < Cases.Length; i++)
            {
                int* lol = stackalloc int[Cases.Length];
                villes[i] = lol;
                for (int j = 0; j < Cases.Length; j++)
                {
                    Position tmp = new Position(i, j);
                    villes[i][j] = ((Villes.ContainsKey(tmp)) ? 0 : 1);
                }
            }

            int taille = Cases.Length;

            int** res = ws.suggerer(cases, ennemis, villes, taille);

            for (int i = 0; i < taille; i++)
            {
                for (int j = 0; j < taille; j++)
                {
                    if (res[i][j] == 1) lres.Add(new Position(i,j));
                }
            }

            return lres;

        }


        /**
         * \fn public void setStrategie(typeStrategie s)
         * \brief Definit la strategie de la carte
         * \param s la strategie
         */
        public void setStrategie(typeStrategie s)
		{
            switch (s)
            {
                case typeStrategie.petiteCarte: _strat = new StrategiePetiteCarte(); break;
                case typeStrategie.moyenneCarte: _strat = new StrategieMoyenneCarte(); break;
                case typeStrategie.grandeCarte: _strat = new StrategieGrandeCarte(); break;
                default: break;
            }
		}


        /**
         * \fn public static void ajoutUnite(Unite e)
         * \brief Ajoute une unite au hachage _unites
         * \param e l'unite a ajouter
         */
        public static void ajoutUnite(Unite e)
        {
            if (! Unites.ContainsKey(e.Posit))
            {
                _unites.Add(e.Posit, new List<Unite>());

            }
            ((List<Unite>)_unites[e.Posit]).Add(e);
        }


        /**
         * \fn public static void suppressionUnite(Unite e)
         * \brief Supprime une unite du hachage _unites
         * \param e l'unite a supprimer
         */
        public static void suppressionUnite(Unite e)
        {
            if (Unites.ContainsKey(e.Posit))
            {
                ((List<Unite>)_unites[e.Posit]).Remove(e);
            }
        }


        /**
         * \fn public static void ajoutVille(Ville v)
         * \brief Ajoute une ville au hachage _villes
         * \param v la ville a ajouter
         */
        public static void ajoutVille(Ville v)
        {
            _villes.Add(v.PositCentrale, v);
        }

        /**
         * \fn public static void suppressionVille(Ville v)
         * \brief Supprime toutes les cases du hachage _villes occupe par la ville v
         * \param v la ville a supprimer
         */
        public static void suppressionVille(Ville v)
        {
            foreach(var item in _villes.Where(kvp => kvp.Value == v).ToList())
            {
                _villes.Remove(item.Key);
            }
        }


        /**
         * \fn public void save(StreamWriter sw)
         * \brief Sauvegarde les cases de la carte
         * \param sw le flux dans lequel ecrire
         */
        public void save(StreamWriter sw)
        {
            sw.Write(Cases.Length);
            sw.WriteLine();
            for (int i = 0; i < Cases.Length; i++)
            {
                for (int j = 0; j < Cases.Length; j++)
                {
                    Case tmp = Cases[i][j];
                    if (tmp is CaseFer)
                    {
                        if (((CaseFer)tmp).Square is Montagne) sw.Write(MONTAGNEFER);
                        else if (((CaseFer)tmp).Square is Desert) sw.Write(DESERTFER);
                        else sw.Write(PLAINEFER);
                    }
                    else if (tmp is CaseFruit)
                    {
                        if (((CaseFruit)tmp).Square is Montagne) sw.Write(MONTAGNEFRUIT);
                        else if (((CaseFruit)tmp).Square is Desert) sw.Write(DESERTFRUIT);
                        else sw.Write(PLAINEFRUIT);
                    }
                    else
                    {
                        if (tmp is Montagne) sw.Write(MONTAGNE);
                        else if (tmp is Desert) sw.Write(DESERT);
                        else sw.Write(PLAINE);
                    }
                }//for j
            }//for i
            sw.WriteLine();
        }//load


        /**
         * \fn public void load(StreamReader sr)
         * \brief Charge les cases de la carte
         * \param sr le flux dans lequel lire
         */
        public void load(StreamReader sr)
        {
            _villes.Clear();
            _unites.Clear();
            int taille = Convert.ToInt32(sr.ReadLine());
            _cases = new Case[taille][];
            switch (taille)
            {
                case 25: this.setStrategie(typeStrategie.petiteCarte); break;
                case 50: this.setStrategie(typeStrategie.moyenneCarte); break;
                default: this.setStrategie(typeStrategie.grandeCarte); break;
            }
            for (int i = 0; i < taille; i++)
            {
                _cases[i] = new Case[taille];
                for (int j = 0; j < taille; j++)
                {
                    char c = (char)sr.Read();
                    switch (c)
                    {
                        case PLAINE:
                            _cases[i][j] = FabriquePoidsMoucheCase.INSTANCE.getPlaine();break;
                        case MONTAGNE:
                            _cases[i][j] = FabriquePoidsMoucheCase.INSTANCE.getMontagne(); break;
                        case DESERT:
                            _cases[i][j] = FabriquePoidsMoucheCase.INSTANCE.getDesert(); break;
                        case PLAINEFER:
                            _cases[i][j] = FabriquePoidsMoucheCase.INSTANCE.getPlaineFer(); break;
                        case MONTAGNEFER:
                            _cases[i][j] = FabriquePoidsMoucheCase.INSTANCE.getMontagneFer(); break;
                        case DESERTFER:
                            _cases[i][j] = FabriquePoidsMoucheCase.INSTANCE.getDesertFer(); break;
                        case PLAINEFRUIT:
                            _cases[i][j] = FabriquePoidsMoucheCase.INSTANCE.getPlaineFruit(); break;
                        case MONTAGNEFRUIT:
                            _cases[i][j] = FabriquePoidsMoucheCase.INSTANCE.getMontagneFruit(); break;
                        default:
                            _cases[i][j] = FabriquePoidsMoucheCase.INSTANCE.getDesertFruit(); break;
                    }//switch-case
                }//for j
            }//for i
            sr.ReadLine();
        }//load

    }
}

